﻿using KBEngine;
using UnityEngine;
using System; 
using System.IO;  
using System.Collections; 
using System.Collections.Generic;
using System.Linq;

public class World : MonoBehaviour
{
    public static World Instance;

    private GameObject m_terrain;
    public GameObject m_terrainPerfab;

    public GameObject m_playerPerfab;
    public GameObject m_entityPerfab;

    private ObjGUID m_playerFullGuid;
    private ObjGUID m_playerGuid;
    private InstGUID m_instGuid;

    private Dictionary<ObjGUID, LocatableObjectController> m_allObjs = new Dictionary<ObjGUID, LocatableObjectController>();

    public GameObject HeroGameObject { get; private set; }
    public HeroController Hero { get; private set; }

    void Awake()
    {
        Instance = this;
    }

    void Start()
    {
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_PLAYER_LOAD_MAP, pck => HandleLoadMap(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_PLAYER_ENTER_MAP, pck => HandleEnterMap(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_PLAYER_LEAVE_MAP, pck => HandleLeaveMap(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_LOC_TELEPORT, pck => HandleLocalTeleport(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_CREATE_OBJECT, pck => HandleCreateObject(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_REMOVE_OBJECT, pck => HandleRemoveObject(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_VALUE32_UPDATE, pck => HandleValue32Update(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_VALUE64_UPDATE, pck => HandleValue64Update(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_SYNC_MOVE, pck => HandleUnitSyncMove(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_STOP_MOVE, pck => HandleUnitStopMove(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_SYNC_TURN, pck => HandleUnitSyncTurn(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_STOP_TURN, pck => HandleUnitStopTurn(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_DEAD, pck => HandleUnitDead(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_REVIVE, pck => HandleUnitRevive(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UNIT_STRIKE, pck => HandleUnitStrike(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_CREATE_QUEST, pck => HandleCreateQuest(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_REMOVE_QUEST, pck => HandleRemoveQuest(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_UPDATE_QUEST, pck => HandleUpdateQuest(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_SUBMIT_QUEST, pck => HandleSubmitQuest(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_QUEST_FINISH, pck => HandleQuestFinish(pck));
        GameSession.Instance.RegisterHandler(GAME_OPCODE.SMSG_QUEST_FAILED, pck => HandleQuestFailed(pck));
    }

    void Update()
    {
        if (Hero != null && Input.GetMouseButton(0))
        {
            var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                var obj = hit.collider.gameObject.GetComponent<LocatableObjectController>();
                if (obj != null)
                {
                    if (obj.IsKindOf(OBJECT_TYPE.TYPE_UNIT))
                    {
                        Hero.TargetGuid = obj.GetGuid();
                        GameSession.Instance.SendSpellCast(1,
                            Hero.TargetGuid, Hero.Position, obj.Position - Hero.Position);
                    }
                    else if (obj.IsType(OBJECT_TYPE.TYPE_STATICOBJECT))
                    {
                        GameSession.Instance.SendObjectInteractive(
                            obj.GetGuid(), Hero.Position, obj.Position - Hero.Position);
                    }
                }
            }
        }
    }

    public void HandleLoadMap(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out m_playerGuid);
        pckEx.Read(out m_instGuid);
        pckEx.Read(out Vector3 pos);

        Debug.Assert(m_terrain == null);
        m_terrain = Instantiate(m_terrainPerfab);

        var transform = HeroGameObject.transform;
        transform.position = pos;

        GameSession.Instance.SendLoadMapFinish();
    }

    public void HandleLeaveMap(NetPacket pck)
    {
        m_terrain = null;
        m_allObjs.Remove(Hero.GetGuid());
        Destroy(Hero);
        Hero = null;
        foreach (var pair in m_allObjs) {
            Destroy(pair.Value.gameObject);
        }
        m_allObjs.Clear();
    }

    public void HandleLocalTeleport(NetPacket pck)
    {
        m_allObjs.Remove(Hero.GetGuid());
        Destroy(Hero);
        Hero = null;
        GameSession.Instance.SendLocalTeleportFinish();
    }

    public void HandleEnterMap(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out m_playerFullGuid);
        pckEx.Read(out m_instGuid);
        pckEx.Read(out Vector3 pos);
        pckEx.Read(out Vector3 dir);

        var transform = HeroGameObject.transform;
        transform.position = pos;
        transform.LookAt(dir);

        UI.Instance.info("进入地图!");
    }

    public void HandleCreateObject(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);

        LocatableObjectController obj = null;
        if (objGuid == m_playerFullGuid)
        {
            obj = Hero = HeroGameObject.AddComponent<HeroController>();
        }
        else
        {
            var entity = Instantiate(m_entityPerfab);
            switch ((OBJECT_TYPE)objGuid.TID)
            {
                case OBJECT_TYPE.TYPE_STATICOBJECT:
                    obj = entity.AddComponent<StaticObjectController>();
                    break;
                case OBJECT_TYPE.TYPE_AURAOBJECT:
                    obj = entity.AddComponent<AuraObjectController>();
                    break;
                case OBJECT_TYPE.TYPE_CREATURE:
                    obj = entity.AddComponent<CreatureController>();
                    break;
                case OBJECT_TYPE.TYPE_PLAYER:
                    obj = entity.AddComponent<PlayerController>();
                    break;
                case OBJECT_TYPE.TYPE_AUTOPLAYER:
                    obj = entity.AddComponent<AutoPlayerController>();
                    break;
            }
        }
        obj.Init(objGuid);
        obj.LoadFromCreatePacket(pck);
        m_allObjs.Add(objGuid, obj);
    }

    public void HandleRemoveObject(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);

        var obj = m_allObjs[objGuid];
        m_allObjs.Remove(objGuid);
        Destroy(obj.gameObject);
    }

    public void HandleValue32Update(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);

        var obj = m_allObjs[objGuid];
        obj.ReadValue32UpdatePacket(pck);
    }

    public void HandleValue64Update(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);

        var obj = m_allObjs[objGuid];
        obj.ReadValue64UpdatePacket(pck);
    }

    public void HandleUnitSyncMove(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);
        pckEx.Read(out Vector3 pos);
        pckEx.Read(out Vector3 dir);

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Position = pos;
        obj.Direction = dir;
    }

    public void HandleUnitStopMove(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);
        pckEx.Read(out Vector3 pos);
        pckEx.Read(out Vector3 dir);

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Position = pos;
        obj.Direction = dir;
    }

    public void HandleUnitSyncTurn(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);
        pckEx.Read(out Vector3 pos);
        pckEx.Read(out Vector3 dir);

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Position = pos;
        obj.Direction = dir;
    }

    public void HandleUnitStopTurn(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);
        pckEx.Read(out Vector3 pos);
        pckEx.Read(out Vector3 dir);

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Position = pos;
        obj.Direction = dir;
    }

    public void HandleUnitDead(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);
        string killerName = null;
        if (!pck.IsReadableEmpty())
        {
            pckEx.Read(out ObjGUID killerGuid);
            pck.Read(out killerName);
        }

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Dead();

        UI.Instance.info(string.Format("`{0}`被`{1}`杀死了.", obj.GetName(), killerName));
    }

    public void HandleUnitRevive(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGuid);

        var obj = m_allObjs[objGuid] as UnitController;
        obj.Revive();
    }

    public void HandleUnitStrike(NetPacket pck)
    {
        var pckEx = new NetPacketHelper(pck);
        pckEx.Read(out ObjGUID objGUID);
        pckEx.Read(out ObjGUID victimGUID);
        pck.Read(out UInt64 spellInstGuid);
        pck.Read(out UInt32 spellID);
        pck.Read(out UInt32 spellLevel);
        pck.Read(out UInt16 sleIdx);
        pck.Read(out UInt64 strikeValue);

        if (!m_allObjs.TryGetValue(objGUID, out LocatableObjectController obj))
        {
            return;
        }
        if (!m_allObjs.TryGetValue(victimGUID, out LocatableObjectController victim))
        {
            return;
        }
        var spellName = DBMgr.Instance.GetText(spellID, STRING_TEXT_TYPE.SPELL_NAME);
        var objName = (obj as UnitController).GetName();
        var victimName = (victim as UnitController).GetName();
        UI.Instance.info(string.Format("`{0}`用`{1}`攻击`{2}`,失血`{3}`.",
            objName, spellName, victimName, strikeValue));
    }

    public void HandleCreateQuest(NetPacket pck)
    {
        var pQuestLog = new QuestLog();
        pQuestLog.LoadFromCreatePacket(pck);
        Hero.QuestStorage.AddQuest(pQuestLog);
        UI.Instance.PushPlayMsg(string.Format("创建任务`{0}`.", pQuestLog.GetName()));
    }

    public void HandleRemoveQuest(NetPacket pck)
    {
        pck.Read(out UInt32 questGuid);
        var pQuestLog = Hero.QuestStorage.GetQuest(questGuid);
        if (pQuestLog != null)
        {
            UI.Instance.PushPlayMsg(string.Format("移除任务`{0}`.", pQuestLog.GetName()));
            Hero.QuestStorage.RemoveQuest(questGuid);
        }
    }

    public void HandleUpdateQuest(NetPacket pck)
    {
        pck.Read(out UInt32 questGuid);
        var pQuestLog = Hero.QuestStorage.GetQuest(questGuid);
        if (pQuestLog != null)
        {
            pck.AdjustReadPosition(-sizeof(UInt32));
            pQuestLog.LoadFromCreatePacket(pck);
            UI.Instance.PushPlayMsg(string.Format("更新任务`{0}`{1}/{2}.",
                pQuestLog.GetName(),
                pQuestLog.GetQuestProp().questConditions[0].progress,
                pQuestLog.GetQuestProto().questConditions[0].conditionNum));
        }
    }

    public void HandleSubmitQuest(NetPacket pck)
    {
        pck.Read(out UInt32 questGuid);
        var pQuestLog = Hero.QuestStorage.GetQuest(questGuid);
        if (pQuestLog != null)
        {
            UI.Instance.PushPlayMsg(string.Format("提交任务`{0}`.", pQuestLog.GetName()));
            Hero.QuestStorage.RemoveQuest(questGuid);
        }
    }

    public void HandleQuestFinish(NetPacket pck)
    {
        pck.Read(out UInt32 questGuid);
        var pQuestLog = Hero.QuestStorage.GetQuest(questGuid);
        if (pQuestLog != null)
        {
            UI.Instance.PushPlayMsg(string.Format("任务`{0}`完成.", pQuestLog.GetName()));
        }
    }

    public void HandleQuestFailed(NetPacket pck)
    {
        pck.Read(out UInt32 questGuid);
        var pQuestLog = Hero.QuestStorage.GetQuest(questGuid);
        if (pQuestLog != null)
        {
            UI.Instance.PushPlayMsg(string.Format("任务`{0}`失败.", pQuestLog.GetName()));
        }
    }

    public void CreateHero()
	{
        Debug.Assert(HeroGameObject == null);
        HeroGameObject = Instantiate(m_playerPerfab);
        DontDestroyOnLoad(HeroGameObject);
    }

    public LocatableObjectController GetObjectController(ObjGUID guid)
    {
        return m_allObjs.TryGetValue(guid, out LocatableObjectController obj) ? obj : null;
    }
}
